package com.pengxh.app.gb28181.util;

public class G711Encoder {
    private static final short[] SEG_VECTOR = {0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF};

    /**
     * 将 16-bit 线性 PCM 值编码为 G.711 A-law 字节
     *
     * @param pcmVal 输入 PCM 值（16-bit signed，范围 -32768 ~ 32767）
     * @return 编码后的 A-law 字节（作为 byte，实际值需按无符号处理）
     */
    private static byte linear2alaw(short pcmVal) {
        int mask;
        int seg;
        int aval;

        if (pcmVal >= 0) {
            mask = 0xD5; // sign bit = 1 (A-law: positive → bit7=1)
        } else {
            mask = 0x55; // sign bit = 0 (A-law: negative → bit7=0)
            // 注意：-32768 的负数取反会溢出，需特殊处理
            if (pcmVal == Short.MIN_VALUE) {
                pcmVal = 32767;
            } else {
                pcmVal = (short) (-pcmVal - 1);
            }
        }

        // 确定段落（segment）
        seg = 0;
        while (seg < 7 && pcmVal > SEG_VECTOR[seg]) {
            seg++;
        }

        // 组合符号位、段落和量化值
        if (seg >= 8) {
            aval = 0x7F ^ mask;
        } else {
            aval = seg << 4;
            if (seg < 2) {
                aval |= (pcmVal >> 1) & 0x0F;
            } else {
                aval |= (pcmVal >> (seg + 1)) & 0x0F;
            }
            aval ^= mask;
        }

        return (byte) (aval & 0xFF);
    }

    /**
     * 编码 PCM 到 G.711 A-law byte[]
     */
    public static byte[] encode2alaw(byte[] pcm) {
        // 转换为 short[]
        short[] shortTypePcm = new short[pcm.length / 2];
        for (int i = 0; i < shortTypePcm.length; i++) {
            // 小端字序，如果编码后有问题，可以考虑换为大端字序
            shortTypePcm[i] = (short) ((pcm[2 * i] & 0xFF) | (pcm[2 * i + 1] << 8));
        }

        // 线性转 A-law
        byte[] alaw = new byte[shortTypePcm.length];
        for (int i = 0; i < shortTypePcm.length; i++) {
            alaw[i] = linear2alaw(shortTypePcm[i]);
        }
        return alaw;
    }

    /**
     * G.711μ 转 PCM
     */
    public static void encode2μlaw(short[] pcm, byte[] code, int size) {
        for (int i = 0; i < size; i++) {
            pcm[i] = linear2μlaw(code[i]);
        }
    }

    private static short linear2μlaw(byte ulawbyte) {
        ulawbyte = (byte) ~ulawbyte;
        int sign = (ulawbyte & 0x80);
        int exponent = (ulawbyte & 0x70) >> 4;
        int mantissa = ulawbyte & 0x0F;
        int sample = (mantissa << (exponent + 3)) + (0x80 << exponent);
        if (sign != 0) {
            sample = -sample;
        }
        return (short) sample;
    }
}
